﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using System.Collections.ObjectModel;
using Microsoft.Xna.Framework.Graphics;
using System.Threading;

namespace KinectTD.State_Management
{
   public class ScreenStack
    {


        /// <summary>
        /// A reference to the game that we are working with.
        /// Allows us to grab some key data (for the content pipeline and that lark).
        /// </summary>
        private Game activeGame;
        public Game ActiveGame { get { return activeGame; } }

        /// <summary>
        /// The Content Manager we will be using for all of our game screens to handle the content
        /// </summary>
        private ContentManager content;
        public ContentManager Content { get { return content; } }


        /// <summary>
        /// The SpriteBatch object allowing us to draw
        /// </summary>
        private SpriteBatch spriteBatch;
        public SpriteBatch SpriteBatch { get { return spriteBatch; } }

        /// <summary>
        /// The GraphicsDevice object allowing us to handle the graphics
        /// </summary>
        private GraphicsDevice graphics;
        public GraphicsDevice GraphicsDevice { get { return graphics; } }

        private Stack<Screen> screenStack;

        private LoadingScreen loadScreen;

        private Thread loaderThread;

        public ScreenStack(Game game)
        {
            activeGame = game;
            Initialise();
        }


        /// <summary>
        /// Pushes a new screen to the top of the stack
        /// If the game is already loading a screen, anything else pushed to it will be ignored. 
        /// </summary>
        /// <param name="item">The Screen to push onto the stack</param>
        /// <param name="removeLower">Determines whether or not the active screen will be removed</param>
        public void Push(Screen item, bool removeLower = false)
        {
            if (removeLower)
                screenStack.Pop();
            screenStack.Push(loadScreen);
            if (loaderThread == null || !loaderThread.IsAlive)
            {

                loaderThread = new Thread(new ParameterizedThreadStart(LoadScreen));
                loaderThread.Start(item);
            }


        }

       public void Pop()
        {
           if(screenStack.Count > 1)
                screenStack.Pop();
        }

        /// <summary>
        /// Loads the screens content. When completed, removes the loading screen and pushes on the screen we wanted to have loaded.
        /// </summary>
        /// <param name="loader">The screen to load</param>
        private void LoadScreen(object loader)
        {
            Screen item = loader as Screen;
            item.Initialise(Content);
            lock (screenStack)
            {
                screenStack.Pop();
                screenStack.Push(item);
            }
        }
        /// <summary>
        /// Initialises variables
        /// </summary>
        private void Initialise()
        {
            screenStack = new Stack<Screen>();
            content = new ContentManager(activeGame.Content.ServiceProvider);
            Content.RootDirectory = "Content";

            graphics = activeGame.GraphicsDevice;

            spriteBatch = new SpriteBatch(graphics);

            loadScreen = new LoadingScreen(graphics.Viewport.Width, graphics.Viewport.Height);

        }

        public void Update(GameTime gameTime)
        {
            if (screenStack.Count > 0)
                screenStack.Peek().Update(gameTime);


        }

        public void Draw()
        {
            if (screenStack.Count > 0)
            {
                spriteBatch.Begin();

                //If the screen on top is slightly transparent we need to draw the ones below it as well.
                if (screenStack.Peek().ScreenAlpha != 1)
                {
                    //Keep drawing all of the screens until the alpha is one (if the screen doesn't take up the full viewport size, tough).
                    foreach (Screen s in screenStack)
                    {
                        s.Draw(spriteBatch);
                        if (s.ScreenAlpha == 1) break;
                    }
                }
                else
                {
                    //Draw a normal screen with no overlay.
                    screenStack.Peek().Draw(spriteBatch);
                }
                spriteBatch.End();
            }
        }
    }

    class LoadingScreen : Screen
    {


        private Vector2 position;
        private string loadingText;
        private float timer = 0;
        private int dots = 0;
        public LoadingScreen(int width, int height)
            : base(width, height)
        {

            loadingText = "Loading";
            position = new Vector2(width / 2, height / 2);

            Vector2 length = Engine.UtilFont.MeasureString(loadingText);
            position = position - length;
            ScreenAlpha = 0.5f;

        }

        public override void Update(GameTime gameTime)
        {

            timer += (float)gameTime.ElapsedGameTime.TotalSeconds;
            if (dots == 3)
            {
                dots = 0;
                loadingText = "Loading";
            }

            if (timer >= 0.5)
            {
                dots++;
                timer = 0;
                for (int i = 0; i < dots; i++)
                {
                    loadingText += ".";
                }

            }
        }


        public override void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(Engine.TestTexture, new Rectangle(0, 0, spriteBatch.GraphicsDevice.Viewport.Width, spriteBatch.GraphicsDevice.Viewport.Height), new Color(0, 0, 0, ScreenAlpha));
            spriteBatch.DrawString(Engine.UtilFont, loadingText, position, Color.White);

        }

        protected override void LoadContent(ContentManager Content)
        {
            //Required to completely implement abstract class "Screen"
            //Could you imagine loading extra stuff to cover up hiding stuff. 
        }
    }
}
